/*
 * NodoBSharpChar.cpp
 *
 *  Created on: 01/10/2010
 *      Author: jijodra
 */

#include "NodoBSharpString.h"

const short NodoBSharpString::NODO_RAIZ = 1;
const short NodoBSharpString::NODO_HOJA = 2;
const short NodoBSharpString::NODO_INDICE = 3;

NodoBSharpString::NodoBSharpString() {

	this->cantClavesUsadas = 0;
	this->cantRegistrosUsados = 0;
	this->numNodo = 0;
	this->numBloque = 0;
	this->anterior.numBloque = -1;
	this->anterior.numNodo = -1;
	this->siguiente.numBloque = -1;
	this->siguiente.numNodo = -1;
}

NodoBSharpString::NodoBSharpString(short tipo){

	this->tipo = tipo;
	this->cantClavesUsadas = 0;
	this->cantRegistrosUsados = 0;
	this->numNodo = 0;
	this->numBloque = 0;
	this->anterior.numBloque = -1;
	this->anterior.numNodo = -1;
	this->siguiente.numBloque = -1;
	this->siguiente.numNodo = -1;
}

void NodoBSharpString::Salvar(char* fileName) {

	/* Se guarda en el siguiente orden:
	 * 		longitud del nodo
	 * 		numNodo
	 * 		numBloque
	 * 		tipo
	 * 		cantClavesUsadas
	 * 			todas las claves (de haber)
	 * 		cantPunteros
	 * 			todos los punteros (de haber) [numBloque numnodo]
	 * 		cantRegistrosUsados
	 * 			todos los registros (de haber)
	 * 		siguiente [numBloque numnodo]
	 */

	//Crea un bloque nuevo y le guarda el nodo
	ManejadorAccesoDatos manejadorDatos(fileName, ABRIR);
	char* bloque;
	long cantidadNodosEnBloque = 0;
	long tamanioBloque = manejadorDatos.getTamanioBloques();
	long espacioLibreBloque = 0;
	long longitud = 0;

	//Verifico si el bloque en el que tengo que grabar es nuevo o no
	if(this->numBloque > manejadorDatos.getCantidadBloques() - 1)
	{
		bloque = manejadorDatos.inicillizarBloque(tamanioBloque);
		char* bloqueIterador = bloque;

		longitud = this->CalcularTamanio(*this);
		cantidadNodosEnBloque = 1;
		espacioLibreBloque = tamanioBloque - longitud - 12 /*12 = cantidad nodos + espacio Libre + longitud nodo*/;

		//Guardo la cantidad de nodos en el bloque y espacio libre
		memcpy(bloqueIterador, &(cantidadNodosEnBloque), sizeof(cantidadNodosEnBloque));
		bloqueIterador+=sizeof(cantidadNodosEnBloque);

		memcpy(bloqueIterador, &(espacioLibreBloque), sizeof(espacioLibreBloque));
		bloqueIterador+=sizeof(espacioLibreBloque);

		//Guardo el tamanio del nodo
		memcpy(bloqueIterador, &(longitud), sizeof(longitud));
		bloqueIterador+=sizeof(longitud);

		//Guardo numero de bloque y numero de nodo de este nodo
		memcpy(bloqueIterador, &(this->numNodo), sizeof(this->numNodo));
		bloqueIterador+=sizeof(this->numNodo);

		memcpy(bloqueIterador, &(this->numBloque), sizeof(this->numBloque));
		bloqueIterador+=sizeof(this->numBloque);

		//Guardo el tipo
		memcpy(bloqueIterador, &(this->tipo), sizeof(this->tipo));
		bloqueIterador+=sizeof(this->tipo);

		//Guardo cantidad de claves usadas y sus claves a continuacion
		memcpy(bloqueIterador, &(this->cantClavesUsadas), sizeof(this->cantClavesUsadas));
		bloqueIterador+=sizeof(this->cantClavesUsadas);

		if(this->cantClavesUsadas > 0)
		{
			for(int i = 0; i < cantClavesUsadas; i++)
			{
				memcpy(bloqueIterador, claves[i].c_str(), claves[i].length());
				bloqueIterador+=claves[i].length();
			}
		}

		//Guardos los punteros
		int cantidadPunteros = (int)punteros.size();
		memcpy(bloqueIterador, &(cantidadPunteros), sizeof(cantidadPunteros));
		bloqueIterador+=sizeof(cantidadPunteros);

		if(cantidadPunteros > 0)
		{
			for(int i = 0; i < cantidadPunteros; i++)
			{
				Puntero puntero = punteros[i];

				memcpy(bloqueIterador, &(puntero.numBloque), sizeof(puntero.numBloque));
				bloqueIterador+=sizeof(puntero.numBloque);

				memcpy(bloqueIterador, &(puntero.numNodo), sizeof(puntero.numNodo));
				bloqueIterador+=sizeof(puntero.numNodo);
			}
		}

		//Guardo los registros
		memcpy(bloqueIterador, &(this->cantRegistrosUsados), sizeof(this->cantRegistrosUsados));
		bloqueIterador+=sizeof(this->cantRegistrosUsados);

		if(this->cantRegistrosUsados > 0)
		{
			for(int i = 0; i < this->cantRegistrosUsados; i++)
			{
				RegistroBSharpString registro = registros[i];

				memcpy(bloqueIterador, registro.clave.c_str(), registro.clave.length());
				bloqueIterador+=registro.clave.length();

				memcpy(bloqueIterador, &(registro.valor), sizeof(registro.valor));
				bloqueIterador+=sizeof(registro.valor);
			}
		}

		//Guardo el anterior. Si no es hoja lo tiene en -1
		memcpy(bloqueIterador, &(this->anterior.numBloque), sizeof(this->anterior.numBloque));
		bloqueIterador+=sizeof(this->anterior.numBloque);

		memcpy(bloqueIterador, &(this->anterior.numNodo), sizeof(this->anterior.numNodo));
		bloqueIterador+=sizeof(this->anterior.numNodo);

		//Guardo el siguiente. Si no es hoja lo tiene en -1
		memcpy(bloqueIterador, &(this->siguiente.numBloque), sizeof(this->siguiente.numBloque));
		bloqueIterador+=sizeof(this->siguiente.numBloque);

		memcpy(bloqueIterador, &(this->siguiente.numNodo), sizeof(this->siguiente.numNodo));
		bloqueIterador+=sizeof(this->siguiente.numNodo);

		manejadorDatos.escribir(bloque, this->numBloque);
	}
	else
	{
		//Obtiene el bloque y rearma todo el bloque entero con el nodo modificado
		//Asumimos que si llega aca es porque entra en el nodo
		ManejadorAccesoDatos manejadorDatos = ManejadorAccesoDatos(fileName, ABRIR);

		bloque = manejadorDatos.leerBloque(this->numBloque);
		char* bloqueIterador = bloque;
		long cantidadNodosEnBloque = 0;
		int cantidadPunteros = 0;
		bool existeNodo = false;
		int posicionExistente = 0;

		memcpy(&(cantidadNodosEnBloque), bloqueIterador, sizeof(cantidadNodosEnBloque));
		bloqueIterador+=sizeof(cantidadNodosEnBloque);
		bloqueIterador+=(sizeof(long)); //Salteo el espacio libre del bloque

		vector<NodoBSharpString> nodos;

		//Cargo todos los nodos en un vector
		for(int j = 0; j < cantidadNodosEnBloque; j++){

			bloqueIterador+=(sizeof(long)); //Salteo la longitud del bloque

			NodoBSharpString nodo;

			//Sino cargo el nodo
			memcpy(&(nodo.numNodo), bloqueIterador, sizeof(nodo.numNodo));
			bloqueIterador+=sizeof(nodo.numNodo);

			//Guardo un flag para saber si el nodo ya existia y en que posicion debo reemplazarlo
			if(nodo.numNodo == this->numNodo)
			{
				existeNodo = true;
				posicionExistente = j;
			}

			//Leo el numero de bloque
			memcpy(&(nodo.numBloque), bloqueIterador, sizeof(nodo.numBloque));
			bloqueIterador+=sizeof(nodo.numBloque);

			//Leo el tipo
			memcpy(&(nodo.tipo), bloqueIterador, sizeof(nodo.tipo));
			bloqueIterador+=sizeof(nodo.tipo);

			//Guardo cantidad de claves usadas y sus claves a continuacion
			memcpy(&(nodo.cantClavesUsadas), bloqueIterador, sizeof(nodo.cantClavesUsadas));
			bloqueIterador+=sizeof(nodo.cantClavesUsadas);

			if(nodo.cantClavesUsadas > 0)
			{
				char claveAux[201];
				string claveStringAux = "";

				for(int i = 0; i < nodo.cantClavesUsadas; i++)
				{
					//Probarlo, sino utilizar el push_back
					claveAux[200] = '\0';
					memcpy(&(claveAux), bloqueIterador, 200);
					bloqueIterador+=200;

					claveStringAux.assign(claveAux);

					nodo.claves.push_back(claveStringAux);
				}
			}

			//Leo la cantidad de punteros
			memcpy(&(cantidadPunteros), bloqueIterador, sizeof(cantidadPunteros));
			bloqueIterador+=sizeof(cantidadPunteros);

			if(cantidadPunteros > 0)
			{
				for(int i = 0; i < cantidadPunteros; i++)
				{
					Puntero puntero;

					memcpy(&(puntero.numBloque), bloqueIterador, sizeof(puntero.numBloque));
					bloqueIterador+=sizeof(puntero.numBloque);

					memcpy(&(puntero.numNodo), bloqueIterador, sizeof(puntero.numNodo));
					bloqueIterador+=sizeof(puntero.numNodo);

					nodo.punteros.push_back(puntero);
				}
			}

			//Leo los registros
			memcpy(&(nodo.cantRegistrosUsados), bloqueIterador, sizeof(nodo.cantRegistrosUsados));
			bloqueIterador+=sizeof(nodo.cantRegistrosUsados);

			char aux2[201];

			if(nodo.cantRegistrosUsados > 0)
			{
				for(int i = 0; i < nodo.cantRegistrosUsados; i++)
				{
					RegistroBSharpString registro;
					aux2[200] = '\0';
					memcpy(&(aux2), bloqueIterador, 200);
					bloqueIterador+=200;
					registro.clave.assign(aux2);

					memcpy(&(registro.valor), bloqueIterador, sizeof(registro.valor));
					bloqueIterador+=sizeof(registro.valor);

					nodo.registros.push_back(registro);
				}
			}

			//Cargo el anterior. Si no es hoja lo tiene en -1
			memcpy(&(nodo.anterior.numBloque), bloqueIterador, sizeof(nodo.anterior.numBloque));
			bloqueIterador+=sizeof(nodo.anterior.numBloque);

			memcpy(&(nodo.anterior.numNodo), bloqueIterador, sizeof(nodo.anterior.numNodo));
			bloqueIterador+=sizeof(nodo.anterior.numNodo);

			//Cargo el siguiente. Si no es hoja lo tiene en -1
			memcpy(&(nodo.siguiente.numBloque), bloqueIterador, sizeof(nodo.siguiente.numBloque));
			bloqueIterador+=sizeof(nodo.siguiente.numBloque);

			memcpy(&(nodo.siguiente.numNodo), bloqueIterador, sizeof(nodo.siguiente.numNodo));
			bloqueIterador+=sizeof(nodo.siguiente.numNodo);

			nodos.push_back(nodo);
		}

		//Si existia el nodo lo reemplazo, sino lo agrego al final
		if(existeNodo)
			nodos[posicionExistente] = *this;
		else
			nodos.push_back(*this);

		//FINALMENTE, guardo todos los nodos en el bloque
		free(bloque);
		bloque = manejadorDatos.inicillizarBloque(tamanioBloque);
		bloqueIterador = bloque;

		longitud = this->CalcularTamanio(nodos);
		cantidadNodosEnBloque = nodos.size();
		espacioLibreBloque = tamanioBloque - longitud - 8  - (nodos.size() * 4)/*12 = cantidad nodos + espacio Libre */;

		//Guardo la cantidad de nodos en el bloque y espacio libre
		memcpy(bloqueIterador, &(cantidadNodosEnBloque), sizeof(cantidadNodosEnBloque));
		bloqueIterador+=sizeof(cantidadNodosEnBloque);

		memcpy(bloqueIterador, &(espacioLibreBloque), sizeof(espacioLibreBloque));
		bloqueIterador+=sizeof(espacioLibreBloque);

		//Guardo cada nodo
		for(int k = 0; k < nodos.size(); k++)
		{
			//Guardo el tamanio del nodo
			long tamanioTotal = this->CalcularTamanio(nodos[k]);
			memcpy(bloqueIterador, &(tamanioTotal), sizeof(tamanioTotal));
			bloqueIterador+=sizeof(tamanioTotal);

			//Guardo numero de bloque y numero de nodo de este nodo
			memcpy(bloqueIterador, &(nodos[k].numNodo), sizeof(nodos[k].numNodo));
			bloqueIterador+=sizeof(nodos[k].numNodo);

			memcpy(bloqueIterador, &(nodos[k].numBloque), sizeof(nodos[k].numBloque));
			bloqueIterador+=sizeof(nodos[k].numBloque);

			//Guardo el tipo
			memcpy(bloqueIterador, &(nodos[k].tipo), sizeof(nodos[k].tipo));
			bloqueIterador+=sizeof(nodos[k].tipo);

			//Guardo cantidad de claves usadas y sus claves a continuacion
			memcpy(bloqueIterador, &(nodos[k].cantClavesUsadas), sizeof(nodos[k].cantClavesUsadas));
			bloqueIterador+=sizeof(nodos[k].cantClavesUsadas);

			if(nodos[k].cantClavesUsadas > 0)
			{
				for(int i = 0; i < nodos[k].cantClavesUsadas; i++)
				{
					memcpy(bloqueIterador, nodos[k].claves[i].c_str(), nodos[k].claves[i].length());
					bloqueIterador+=nodos[k].claves[i].length();
				}
			}

			//Guardos los punteros
			int cantidadPunteros = (int)nodos[k].punteros.size();
			memcpy(bloqueIterador, &(cantidadPunteros), sizeof(cantidadPunteros));
			bloqueIterador+=sizeof(cantidadPunteros);

			if(cantidadPunteros > 0)
			{
				for(int i = 0; i < cantidadPunteros; i++)
				{
					Puntero puntero = nodos[k].punteros[i];

					memcpy(bloqueIterador, &(puntero.numBloque), sizeof(puntero.numBloque));
					bloqueIterador+=sizeof(puntero.numBloque);

					memcpy(bloqueIterador, &(puntero.numNodo), sizeof(puntero.numNodo));
					bloqueIterador+=sizeof(puntero.numNodo);
				}
			}

			//Guardo los registros
			memcpy(bloqueIterador, &(nodos[k].cantRegistrosUsados), sizeof(nodos[k].cantRegistrosUsados));
			bloqueIterador+=sizeof(nodos[k].cantRegistrosUsados);

			if(nodos[k].cantRegistrosUsados > 0)
			{
				RegistroBSharpString registro;

				for(int i = 0; i < nodos[k].cantRegistrosUsados; i++)
				{
					registro = nodos[k].registros[i];

					memcpy(bloqueIterador, registro.clave.c_str(), registro.clave.length());
					bloqueIterador+=registro.clave.length();

					memcpy(bloqueIterador, &(registro.valor), sizeof(registro.valor));
					bloqueIterador+=sizeof(registro.valor);
				}
			}

			//Guardo el anterior. Si no es hoja lo tiene en -1
			memcpy(bloqueIterador, &(nodos[k].anterior.numBloque), sizeof(nodos[k].anterior.numBloque));
			bloqueIterador+=sizeof(nodos[k].anterior.numBloque);

			memcpy(bloqueIterador, &(nodos[k].anterior.numNodo), sizeof(nodos[k].anterior.numNodo));
			bloqueIterador+=sizeof(nodos[k].anterior.numNodo);

			//Guardo el siguiente. Si no es hoja lo tiene en -1
			memcpy(bloqueIterador, &(nodos[k].siguiente.numBloque), sizeof(nodos[k].siguiente.numBloque));
			bloqueIterador+=sizeof(nodos[k].siguiente.numBloque);

			memcpy(bloqueIterador, &(nodos[k].siguiente.numNodo), sizeof(nodos[k].siguiente.numNodo));
			bloqueIterador+=sizeof(nodos[k].siguiente.numNodo);
		}

		manejadorDatos.escribir(bloque, this->numBloque);
	}

	free(bloque);
}

//PRE: el nodo existe
void NodoBSharpString::Obtener(char* fileName, Puntero puntero) {

	ManejadorAccesoDatos manejadorDatos(fileName, ABRIR);

	char* bloque = manejadorDatos.leerBloque(puntero.numBloque);
	char* bloqueIterador = bloque;

	//Busco el comienzo del nodo en el bloque
	bool encontrado = false;
	int cantidadNodosLeidos = 0;
	long longitud = 0;
	long numeroNodo = 0;
	long cantidadNodosEnBloque = 0;

	//Leo la cantidad de nodos en el bloque
	memcpy(&(cantidadNodosEnBloque), bloqueIterador, sizeof(cantidadNodosEnBloque));
	bloqueIterador+=sizeof(cantidadNodosEnBloque);
	bloqueIterador+=sizeof(long); //Salteo el espacio libre en el bloque

	while(!encontrado && cantidadNodosLeidos < cantidadNodosEnBloque){
		memcpy(&(longitud), bloqueIterador, sizeof(longitud));
		bloqueIterador+=sizeof(longitud);

		memcpy(&(numeroNodo), bloqueIterador, sizeof(numeroNodo));
		bloqueIterador+=sizeof(numeroNodo);

		if(numeroNodo == puntero.numNodo)
			encontrado = true;
		else
			bloqueIterador+=longitud - sizeof(numeroNodo);

		cantidadNodosLeidos++;
	}

	//Si no lo encontre, limpio el nodo
	if(!encontrado)
	{
		this->Limpiar();
	}
	else
	{ 	//Sino cargo el nodo
		this->numNodo = numeroNodo;

		//Leo el numero de bloque
		memcpy(&(this->numBloque), bloqueIterador, sizeof(this->numBloque));
		bloqueIterador+=sizeof(this->numBloque);

		//Leo el tipo
		memcpy(&(this->tipo), bloqueIterador, sizeof(this->tipo));
		bloqueIterador+=sizeof(this->tipo);

		//Guardo cantidad de claves usadas y sus claves a continuacion
		memcpy(&(this->cantClavesUsadas), bloqueIterador, sizeof(this->cantClavesUsadas));
		bloqueIterador+=sizeof(this->cantClavesUsadas);

		if(this->cantClavesUsadas > 0)
		{
			char claveAux[201];
			string claveStringAux = "";

			for(int i = 0; i < this->cantClavesUsadas; i++)
			{
				//Probarlo, sino utilizar el push_back
				claveAux[200] = '\0';
				memcpy(&(claveAux), bloqueIterador, 200);
				claveStringAux.assign(claveAux);
				bloqueIterador+=200;

				claves.push_back(claveStringAux);
			}
		}

		//Leo la cantidad de punteros
		int cantidadPunteros;
		memcpy(&(cantidadPunteros), bloqueIterador, sizeof(cantidadPunteros));
		bloqueIterador+=sizeof(cantidadPunteros);

		if(cantidadPunteros > 0)
		{
			for(int i = 0; i < cantidadPunteros; i++)
			{
				Puntero puntero;

				memcpy(&(puntero.numBloque), bloqueIterador, sizeof(puntero.numBloque));
				bloqueIterador+=sizeof(puntero.numBloque);

				memcpy(&(puntero.numNodo), bloqueIterador, sizeof(puntero.numNodo));
				bloqueIterador+=sizeof(puntero.numNodo);

				punteros.push_back(puntero);
			}
		}

		//Leo los registros
		memcpy(&(this->cantRegistrosUsados), bloqueIterador, sizeof(this->cantRegistrosUsados));
		bloqueIterador+=sizeof(this->cantRegistrosUsados);

		if(this->cantRegistrosUsados > 0)
		{
			char claveAux2[201];

			for(int i = 0; i < this->cantRegistrosUsados; i++)
			{
				RegistroBSharpString registro;

				claveAux2[200] = '\0';
				memcpy(&(claveAux2), bloqueIterador, 200);
				bloqueIterador+=200;
				registro.clave.assign(claveAux2);

				memcpy(&(registro.valor), bloqueIterador, sizeof(registro.valor));
				bloqueIterador+=sizeof(registro.valor);

				registros.push_back(registro);
			}
		}

		//Obtengo el anterior. Si no es hoja lo tiene con -1
		memcpy(&(this->anterior.numBloque), bloqueIterador, sizeof(this->anterior.numBloque));
		bloqueIterador+=sizeof(this->anterior.numBloque);

		memcpy(&(this->anterior.numNodo), bloqueIterador, sizeof(this->anterior.numNodo));
		bloqueIterador+=sizeof(this->anterior.numNodo);

		//Obtengo el siguiente. Si no es hoja lo tiene con -1
		memcpy(&(this->siguiente.numBloque), bloqueIterador, sizeof(this->siguiente.numBloque));
		bloqueIterador+=sizeof(this->siguiente.numBloque);

		memcpy(&(this->siguiente.numNodo), bloqueIterador, sizeof(this->siguiente.numNodo));
		bloqueIterador+=sizeof(this->siguiente.numNodo);
	}

	free(bloque);
}

long NodoBSharpString::CalcularTamanio(NodoBSharpString nodo){

	long tamanio = 	sizeof(long) + //Tamaño del nodo
					sizeof(nodo.tipo) +
					sizeof(nodo.numNodo) +
					sizeof(nodo.numBloque) +
					sizeof(nodo.cantClavesUsadas) +
					sizeof(int) /*cantidad de Punteros*/ +
					sizeof(nodo.cantRegistrosUsados) +
					(2 * sizeof(long)) /*sizeof(nodo.anterior) */ +
					(2 * sizeof(long)) /*sizeof(nodo.siguiente) */ +
					nodo.claves.size() * 200 +
					nodo.punteros.size() * (2 * sizeof(long)) +
					nodo.registros.size() * (204);

		return tamanio;
}

long NodoBSharpString::CalcularTamanio(vector<NodoBSharpString> nodos){
	long tamanioTotal = 0;
	long tamanioParcial = 0;

	for(int i = 0; i < nodos.size(); i++) {
		tamanioParcial = 	sizeof(long) + //Tamaño del nodo
							sizeof(nodos[i].tipo) +
							sizeof(nodos[i].numNodo) +
							sizeof(nodos[i].numBloque) +
							sizeof(nodos[i].cantClavesUsadas) +
							sizeof(int) /*cantidad de Punteros*/ +
							sizeof(nodos[i].cantRegistrosUsados) +
							(2 * sizeof(long)) /*sizeof(nodos[i].anterior) */+
							(2 * sizeof(long)) /*sizeof(nodos[i].siguiente) */+
							nodos[i].claves.size() * 200 +
							nodos[i].punteros.size() * (2 * sizeof(long)) +
							nodos[i].registros.size() * (204);

		tamanioTotal += tamanioParcial;
	}

	return tamanioTotal;
}

void NodoBSharpString::Limpiar() {

	this->cantClavesUsadas = 0;
	this->cantRegistrosUsados = 0;
	this->numNodo = 0;
	this->numBloque = 0;
	this->tipo = 0;
	this->anterior.numBloque = 0;
	this->anterior.numNodo = 0;
	this->siguiente.numBloque = 0;
	this->siguiente.numNodo = 0;
	this->claves.clear();
	this->punteros.clear();
	this->registros.clear();
}

NodoBSharpString::~NodoBSharpString() {
}


